use serde::{Deserialize, Serialize};
use wither::{
    bson::{doc, oid::ObjectId, DateTime, Document},
    Model,
};

use crate::{utils::strings::text_cut_all, AppError};

use super::{
    user::{NormalUserModel, UserModel},
    Assets,
};

///## 文章板块
/// 将文章分为各种大区块来管理
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct ArticleSectionModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub name: String,
    pub description: Option<String>,
    pub color: String,
    /// 软删除
    pub valid: bool,
    /// 每日进入人数
    pub daily_count: i64,
    /// 上一天的热度
    pub last_day_heat: f64,
    /// 当前热度
    pub heat: f64,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}
impl Default for ArticleSectionModel {
    fn default() -> Self {
        let current = DateTime::now();
        Self {
            id: None,
            name: "noname".to_string(),
            description: None,
            color: "#FFFFFF".to_string(),
            valid: true,
            daily_count: 0,
            last_day_heat: 0.0,
            heat: 0.0,
            created_at: current,
            updated_at: current,
        }
    }
}
/// ## 文章板块对应的DTO
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArticleSectionModelDTO {
    pub id: Option<String>,
    pub name: String,
    pub description: Option<String>,
    pub color: String,
    /// 是否启用
    pub is_enabled: bool,
    pub daily_count: i64,
    pub heat: f64,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl From<ArticleSectionModel> for ArticleSectionModelDTO {
    fn from(m: ArticleSectionModel) -> Self {
        Self {
            id: m.id.as_ref().map(ToString::to_string),
            name: m.name,
            description: m.description,
            color: m.color,
            daily_count: m.daily_count,
            heat: m.heat,
            is_enabled: m.valid,
            created_at: m.created_at.to_chrono(),
            updated_at: m.updated_at.to_chrono(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArticleAuthor {
    pub id: ObjectId,
    pub nickname: String,
    pub avatar: Option<Assets>,
}
impl Default for ArticleAuthor {
    fn default() -> Self {
        Self {
            id: ObjectId::new(),
            nickname: "".to_string(),
            avatar: None,
        }
    }
}
impl TryFrom<UserModel> for ArticleAuthor {
    type Error = AppError;
    fn try_from(value: UserModel) -> Result<Self, Self::Error> {
        Ok(Self {
            id: value
                .id
                .ok_or_else(|| AppError::other_error("require id"))?,
            nickname: value.nickname,
            avatar: value.avatar,
        })
    }
}

impl From<NormalUserModel> for ArticleAuthor {
    fn from(value: NormalUserModel) -> Self {
        Self {
            id: value.id,
            nickname: value.nickname,
            avatar: value.avatar,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArticleAuthorDTO {
    pub id: String,
    pub nickname: String,
    pub avatar: Option<Assets>,
}

impl From<ArticleAuthor> for ArticleAuthorDTO {
    fn from(a: ArticleAuthor) -> Self {
        Self {
            id: a.id.to_string(),
            nickname: a.nickname,
            avatar: a.avatar,
        }
    }
}

/// ## 文章
/// 文章处于某个板块下，由某个作者写出
/// ## 文章的内容结构
/// 结构目前待定
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
#[model(index(keys = r#"doc!{"title_word":"text","description_word":"text"}"#))]
pub struct ArticleModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    /// 文章板块id,或许可以直接把板块嵌进去？
    pub section_id: Option<ObjectId>,
    pub title: String,
    pub title_word: String,
    /// 文章封面首图
    pub cover_image: Option<Assets>,
    /// 文章描述
    pub description: Option<String>,
    pub description_word: Option<String>,
    pub author: ArticleAuthor,
    /// 文章内容结构待定
    pub content: Option<Document>,
    /// 软删除
    pub valid: bool,
    /// 观看人数
    pub watch_count: i64,
    /// 当前热度
    pub heat: f64,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}
impl ArticleModel {
    pub fn update_word(&mut self) {
        self.title_word = text_cut_all(&self.title_word);
        if let Some(ref description) = self.description {
            self.description_word = Some(text_cut_all(description));
        }
    }
}
impl Default for ArticleModel {
    fn default() -> Self {
        let now = DateTime::now();
        Self {
            id: None,
            section_id: None,
            title: "noname".to_string(),
            title_word: "noname".to_string(),
            cover_image: None,
            description: None,
            description_word: None,
            author: ArticleAuthor::default(),
            content: None,
            valid: true,
            watch_count: 0,
            heat: 0.0,
            created_at: now,
            updated_at: now,
        }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArticleModelDTO {
    pub id: Option<String>,
    pub section_id: Option<String>,
    pub title: String,
    pub cover_image: Option<Assets>,
    pub description: Option<String>,
    pub author: ArticleAuthorDTO,
    pub content: Option<Document>,
    pub watch_count: i64,
    pub heat: f64,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl From<ArticleModel> for ArticleModelDTO {
    fn from(m: ArticleModel) -> Self {
        Self {
            id: m.id.as_ref().map(ToString::to_string),
            section_id: m.section_id.as_ref().map(ToString::to_string),
            title: m.title,
            cover_image: m.cover_image,
            description: m.description,
            author: m.author.into(),
            content: m.content,
            watch_count: m.watch_count,
            heat: m.heat,
            created_at: m.created_at.to_chrono(),
            updated_at: m.updated_at.to_chrono(),
        }
    }
}
